package org.momo.rpc.circuitbreaker.impl;

import lombok.extern.slf4j.Slf4j;
import org.momo.rpc.circuitbreaker.CircuitBreakerState;

import java.util.concurrent.atomic.AtomicInteger;

/**
 * @description:
 * @author: momo
 * @date: 2025/4/15 10:42
 */
@Slf4j
public class CircuitBreaker {

    private Integer failThreshold = 10;

    private long retryInterval = 10000L;

    private double halfOpenSuccessRate = 0.05;

    private CircuitBreakerState state = CircuitBreakerState.CLOSED;

    private long lastUpdateTimestamp = System.currentTimeMillis();

    private final AtomicInteger successCount = new AtomicInteger(0);

    private final AtomicInteger failCount = new AtomicInteger(0);

    private final AtomicInteger requestCount = new AtomicInteger(0);

    public CircuitBreaker() {
    }

    public CircuitBreaker(int failThreshold, long retryInterval, double halfOpenSuccessRate) {
        this.failThreshold = failThreshold;
        this.retryInterval = retryInterval;
        this.halfOpenSuccessRate = halfOpenSuccessRate;
    }

    public synchronized boolean allowRequest() {
        switch (state) {
            case OPEN:
                if(System.currentTimeMillis() - lastUpdateTimestamp > retryInterval) {
                    log.warn("Circuit state change from open to half open");
                    this.state = CircuitBreakerState.HALF_OPEN;
                    reset();
                    return true;
                }
                return false;
            case HALF_OPEN:
                requestCount.incrementAndGet();
                return true;
            case CLOSED:
                return true;
        }
        return false;
    }

    public synchronized void recordFailure() {
        failCount.incrementAndGet();
        lastUpdateTimestamp = System.currentTimeMillis();

        if (state == CircuitBreakerState.HALF_OPEN) {
            this.lastUpdateTimestamp = System.currentTimeMillis();
            this.state = CircuitBreakerState.OPEN;
            log.warn("Circuit state change from half open to open");
        } else if (failCount.get() >= failThreshold) {
            this.state = CircuitBreakerState.OPEN;
            log.warn("Circuit state change from closed open to open");
        }
    }

    public synchronized void recordSuccess() {
        if (state == CircuitBreakerState.HALF_OPEN) {
            successCount.incrementAndGet();
            if(successCount.get() >= halfOpenSuccessRate * requestCount.get()) {
                this.state = CircuitBreakerState.CLOSED;
                reset();
                log.warn("Circuit state change from half open to closed");
            }
        }
    }

    private void reset() {
        this.lastUpdateTimestamp = System.currentTimeMillis();
        this.successCount.set(0);
        this.failCount.set(0);
        this.requestCount.set(0);
    }

}

